package lib.quasar.widget.web;

import android.content.Context;
import android.graphics.Bitmap;
import android.net.http.SslError;
import android.os.Build;
import android.os.Environment;
import android.os.Message;
import android.support.annotation.RequiresApi;
import android.text.TextUtils;
import android.util.AttributeSet;
import android.view.View;
import android.webkit.GeolocationPermissions;
import android.webkit.JsPromptResult;
import android.webkit.JsResult;
import android.webkit.SslErrorHandler;
import android.webkit.WebChromeClient;
import android.webkit.WebResourceError;
import android.webkit.WebResourceRequest;
import android.webkit.WebResourceResponse;
import android.webkit.WebSettings;
import android.webkit.WebView;
import android.webkit.WebViewClient;

import lib.quasar.context.BaseConstant;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.util.LinkedList;

import lib.quasar.util.NetUtil;

/**
 * description: WebView
 * created by kalu on 2017/5/25 14:12
 */
public class CusWebView extends WebView {

    private final String TAG = CusWebView.class.getSimpleName();

    // 页面个数
    private final LinkedList<String> htmlCache = new LinkedList();
    /*********************************************************/

    private OnWebChangeListener listener;

    public CusWebView(Context context) {
        this(context, null, 0);
    }

    public CusWebView(Context context, AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public CusWebView(final Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);

        WebSettings setting = getSettings();

        /**
         * 对于不需要使用 file 协议的应用，禁用 file 协议；

         // 禁用 file 协议；
         setAllowFileAccess(false);
         setAllowFileAccessFromFileURLs(false);
         setAllowUniversalAccessFromFileURLs(false);
         对于需要使用 file 协议的应用，禁止 file 协议加载 JavaScript。

         // 需要使用 file 协议
         setAllowFileAccess(true);
         setAllowFileAccessFromFileURLs(false);
         setAllowUniversalAccessFromFileURLs(false);

         // 禁止 file 协议加载 JavaScript
         if (url.startsWith("file://") {
         setJavaScriptEnabled(false);
         } else {
         setJavaScriptEnabled(true);
         }
         */

        // 提高渲染的优先级
        setting.setRenderPriority(WebSettings.RenderPriority.HIGH);

        // 设置自适应屏幕，两者合用
        setting.setUseWideViewPort(true); // 将图片调整到适合webview的大小
        setting.setLoadWithOverviewMode(true); // 缩放至屏幕的大小

        // 缩放操作
        setting.setSupportZoom(true); // 支持缩放，默认为true。是下面那个的前提。
        setting.setBuiltInZoomControls(true); // 设置内置的缩放控件。若为false，则该WebView不可缩放
        setting.setDisplayZoomControls(false); // 隐藏原生的缩放控件

        //其他细节操作
        setting.setAllowFileAccess(true); //设置可以访问文件
        setting.setJavaScriptCanOpenWindowsAutomatically(true); //支持通过JS打开新窗口
        setting.setLoadsImagesAutomatically(true); //支持自动加载图片
        setting.setDefaultTextEncodingName("utf-8");//设置编码格式

        // 设置是否允许通过 file url 加载的 Js代码读取其他的本地文件, 在Android 4.1前默认允许, 在Android 4.1后默认禁止
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
            setting.setAllowFileAccessFromFileURLs(true);
        }
        // 关闭密码保存功能
        setting.setSavePassword(false);

        // 调用JS方法.安卓版本大于17,加上注解 @JavascriptInterface
        setting.setJavaScriptEnabled(true);

        // 1. 缓存模式
        setting.setRenderPriority(WebSettings.RenderPriority.HIGH);  //提高渲染的优先级
        if (NetUtil.isConnected()) {
            setting.setCacheMode(WebSettings.LOAD_DEFAULT);//根据cache-control决定是否从网络上取数据。
        } else {
            setting.setCacheMode(WebSettings.LOAD_CACHE_ELSE_NETWORK);//没网，则从本地获取，即离线加载
        }
        setting.setDomStorageEnabled(true); // 开启 DOM storage API 功能
        setting.setDatabaseEnabled(true);   //开启 database storage API 功能
        setting.setAppCacheEnabled(true); //开启 Application Caches 功能
        setting.setAppCacheMaxSize(20 * 1024 * 1024); // 设置缓存大小
        String cacheDirPath = Environment.getExternalStorageDirectory().getPath() + "//"+ BaseConstant.CACHE_WEB;
        File file = new File(cacheDirPath);
        if (!file.exists()) {
            file.mkdirs();
        }
        setting.setAppCachePath(cacheDirPath); //设置  Application Caches 缓存目录

        // 2.Android5.0 WebView中Http和Https混合问题(Android5.0 WebView中Http和Https混合问题)
//        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
//            // 参数类型说明：
//            // MIXED_CONTENT_ALWAYS_ALLOW：允许从任何来源加载内容，即使起源是不安全的；
//            // MIXED_CONTENT_NEVER_ALLOW：不允许Https加载Http的内容，即不允许从安全的起源去加载一个不安全的资源；
//            // MIXED_CONTENT_COMPATIBILITY_MODE：当涉及到混合式内容时，WebView 会尝试去兼容最新Web浏览器的风格。
//            // 在5.0以下 Android 默认是 全允许，
//            // 但是到了5.0以上，就是不允许，实际情况下很我们很难确定所有的网页都是https的，所以就需要这一步的操作。
//            setting.setMixedContentMode(android.webkit.WebSettings.MIXED_CONTENT_ALWAYS_ALLOW);
//        }

        // 3.设置WebView接受所有网站的证书
        setWebViewClient(new WebViewClient() {

            // 返回false，意味着请求过程里，不管有多少次的跳转请求（即新的请求地址），均交给webView自己处理，这也是此方法的默认处理
            // 返回true，说明你自己想根据url，做新的跳转，比如在判断url符合条件的情况下，我想让webView加载http://ask.csdn.net/questions/178242
            @RequiresApi(api = Build.VERSION_CODES.N)
            @Override
            public boolean shouldOverrideUrlLoading(WebView view, WebResourceRequest request) {

                boolean redirect = request.isRedirect();
                String method = request.getMethod();
                String url = request.getUrl().toString();

                //   LogUtil.e(TAG, "shouldOverrideUrlLoading ==> redirect = " + redirect + ", method = " + method + ", url = " + url);

                if (null != listener) {
                    listener.onWebClickLink(url);
                }
                return super.shouldOverrideUrlLoading(view, request);
            }

            // 多页面在同一个WebView中打开，就是不新建activity或者调用系统浏览器打开
            // 该方法在Build.VERSION_CODES.LOLLIPOP以前有效，从Build.VERSION_CODES.LOLLIPOP起，建议使用shouldOverrideUrlLoading(WebView, WebResourceRequest)} instead
            // 返回false，意味着请求过程里，不管有多少次的跳转请求（即新的请求地址），均交给webView自己处理，这也是此方法的默认处理
            // 返回true，说明你自己想根据url，做新的跳转，比如在判断url符合条件的情况下，我想让webView加载http://ask.csdn.net/questions/178242
            @Override
            public boolean shouldOverrideUrlLoading(WebView view, String url) {

                //   LogUtil.e(TAG, "shouldOverrideUrlLoading ==> url = " + url);
                if (null != listener) {
                    listener.onWebClickLink(url);
                }

                HitTestResult hitTestResult = view.getHitTestResult();
                //hitTestResult==null解决重定向问题
                if (!TextUtils.isEmpty(url) && hitTestResult == null) {
                    view.loadUrl(url);
                    return true;
                }
                return super.shouldOverrideUrlLoading(view, url);
                // view.loadUrl(url);
                // return true;
            }

            @Override
            public void onReceivedError(WebView view, WebResourceRequest request, WebResourceError error) {
                //   LogUtil.e(TAG, "onReceivedError ==>");

//                int errorCode = error.getErrorCode();
//                LogUtil.e(TAG, "onReceivedError ==> error = " + errorCode + ", url = " + request.getUrl());
//
//                switch (errorCode) {
//                    case 404:
//                        view.loadUrl("file:///android_assets/error_handle.html");
//                        break;
//                    default:
//                        super.onReceivedError(view, request, error);
//                        break;
//                }
            }

            @Override
            public void onReceivedHttpError(WebView view, WebResourceRequest request, WebResourceResponse errorResponse) {
                //    LogUtil.e(TAG, "onReceivedHttpError ==>");

//                int errorCode = errorResponse.getStatusCode();
//                LogUtil.e(TAG, "onReceivedHttpError ==> error = " + errorCode + ", url = " + request.getUrl());
//
//                switch (errorCode) {
//                    case 404:
//                        view.loadUrl("file:///android_assets/error_handle.html");
//                        break;
//                    default:
//                        super.onReceivedHttpError(view, request, errorResponse);
//                        break;
//                }
            }

            @Override
            public void onLoadResource(WebView view, String url) {

                int type = view.getHitTestResult().getType();

                //    LogUtil.e(TAG, "onLoadResource ==> url = " + url + ", type = " + type);
                super.onLoadResource(view, url);
            }

            @Override
            public void onPageCommitVisible(WebView view, String url) {
                //    LogUtil.e(TAG, "onPageCommitVisible ==> url = " + url);
                super.onPageCommitVisible(view, url);
            }

            @Override
            public void onPageFinished(WebView view, String url) {

                int type = view.getHitTestResult().getType();

                int size = htmlCache.size();
                if (size == 0) {
                    htmlCache.add(url);
                    if (null != listener) {
                        listener.onWebPageChange(htmlCache.size());
                    }
                } else {
                    if (!htmlCache.contains(url)) {
                        htmlCache.add(url);
                        if (null != listener) {
                            listener.onWebPageChange(htmlCache.size());
                        }
                    }
                }

                //   LogUtil.e(TAG, "onPageFinished ==> url = " + url + ", type = " + type);
                super.onPageFinished(view, url);
            }

            @Override
            public void onReceivedSslError(WebView view, SslErrorHandler handler, SslError error) {
                // handler.cancel();// Android默认的处理方式
                handler.proceed();// 接受所有网站的证书
            }

            // 当向服务器访问这些静态资源时进行拦截，检测到是相同的资源则用本地资源代替
            @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
            @Override
            public WebResourceResponse shouldInterceptRequest(WebView view, WebResourceRequest request) {
                // 步骤1:判断拦截资源的条件，即判断url里的图片资源的文件名
                if (request.getUrl().toString().contains("logo.gif")) {
                    // 假设网页里该图片资源的地址为：http://abc.com/imgage/logo.gif
                    // 图片的资源文件名为:logo.gif

                    InputStream is = null;
                    // 步骤2:创建一个输入流

                    try {
                        is = context.getAssets().open("images/abc.png");
                        // 步骤3:获得需要替换的资源(存放在assets文件夹里)
                        // a. 先在app/src/main下创建一个assets文件夹
                        // b. 在assets文件夹里再创建一个images文件夹
                        // c. 在images文件夹放上需要替换的资源（此处替换的是abc.png图片

                    } catch (IOException e) {
                        e.printStackTrace();
                    }

                    // 步骤4:替换资源
                    WebResourceResponse response = new WebResourceResponse("image/png",
                            "utf-8", is);
                    // 参数1：http请求里该图片的Content-Type,此处图片为image/png
                    // 参数2：编码类型
                    // 参数3：存放着替换资源的输入流（上面创建的那个）
                    return response;
                }
                return super.shouldInterceptRequest(view, request);
            }


            // 当向服务器访问这些静态资源时进行拦截，检测到是相同的资源则用本地资源代替
            @Override
            public WebResourceResponse shouldInterceptRequest(WebView view, String url) {
                // 步骤1:判断拦截资源的条件，即判断url里的图片资源的文件名
                if (url.contains("logo.gif")) {
                    // 假设网页里该图片资源的地址为：http://abc.com/imgage/logo.gif
                    // 图片的资源文件名为:logo.gif

                    InputStream is = null;
                    // 步骤2:创建一个输入流

                    try {
                        is = context.getAssets().open("images/abc.png");
                        // 步骤3:获得需要替换的资源(存放在assets文件夹里)
                        // a. 先在app/src/main下创建一个assets文件夹
                        // b. 在assets文件夹里再创建一个images文件夹
                        // c. 在images文件夹放上需要替换的资源（此处替换的是abc.png图片）

                    } catch (IOException e) {
                        e.printStackTrace();
                    }

                    // 步骤4:替换资源
                    WebResourceResponse response = new WebResourceResponse("image/png",
                            "utf-8", is);
                    // 参数1：http请求里该图片的Content-Type,此处图片为image/png
                    // 参数2：编码类型
                    // 参数3：存放着替换资源的输入流（上面创建的那个）
                    return response;
                }

                return super.shouldInterceptRequest(view, url);
            }
        });

        // 4.多窗口的问题(html中的_bank标签就是新建窗口打开，有时会打不开，需要加以下, 然后 复写 WebChromeClient的onCreateWindow方法)
        setting.setSupportMultipleWindows(false);
        setting.setJavaScriptCanOpenWindowsAutomatically(true);

        setWebChromeClient(new WebChromeClient() {

            /**
             * javascript的警告框
             */
            @Override
            public boolean onJsAlert(WebView view, String url, String message, JsResult result) {
                //  Toast.makeText(context, message, Toast.LENGTH_SHORT).show();
                // return super.onJsAlert(view, url, message, result);
                return true;
            }

            /**
             * javascript的确认框
             */
            @Override
            public boolean onJsConfirm(WebView view, String url, String message, final JsResult result) {
                //    Toast.makeText(context, "javascript的确认框", Toast.LENGTH_SHORT).show();
//                new AlertDialog.Builder(MainActivity.this)
//                        .setTitle("JsConfirm")
//                        .setMessage(message)
//                        .setPositiveButton("OK", new DialogInterface.OnClickListener() {
//                            @Override
//                            public void onClick(DialogInterface dialog, int which) {
//                                result.confirm();
//                            }
//                        })
//                        .setNegativeButton("Cancel", new DialogInterface.OnClickListener() {
//                            @Override
//                            public void onClick(DialogInterface dialog, int which) {
//                                result.cancel();
//                            }
//                        })
//                        .setCancelable(false)
//                        .show();
                // 返回布尔值：判断点击时确认还是取消
                // true表示点击了确认；false表示点击了取消；
                return true;
                // return super.onJsConfirm(view, url, message, result);
            }

            /**
             * javascript输入框
             */
            @Override
            public boolean onJsPrompt(WebView view, String url, String message, String defaultValue, JsPromptResult result) {
                //    Toast.makeText(context, "javascript输入框", Toast.LENGTH_SHORT).show();
                return super.onJsPrompt(view, url, message, defaultValue, result);
            }

            /**
             * 接收到网页上的图片
             */
            @Override
            public void onReceivedTouchIconUrl(WebView view, String url, boolean precomposed) {
                super.onReceivedTouchIconUrl(view, url, precomposed);
            }

            @Override
            public void onProgressChanged(WebView view, int newProgress) {
                //   LogUtil.e(TAG, "onProgressChanged ==> newProgress = " + newProgress);

                if (null != listener) {
                    listener.onWebLoadChange(newProgress);
                }

                super.onProgressChanged(view, newProgress);
            }

            // HTML5定位
            // 需要先加入权限
            // <uses-permission android:name="android.permission.INTERNET"/>
            // <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
            // <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/>
            @Override
            public void onReceivedIcon(WebView view, Bitmap icon) {
                super.onReceivedIcon(view, icon);
            }

            // HTML5定位
            @Override
            public void onGeolocationPermissionsHidePrompt() {
                super.onGeolocationPermissionsHidePrompt();
            }

            // HTML5定位
            @Override
            public void onGeolocationPermissionsShowPrompt(final String origin,
                                                           final GeolocationPermissions.Callback callback) {
                callback.invoke(origin, true, false);//注意个函数，第二个参数就是是否同意定位权限，第三个是是否希望内核记住
                super.onGeolocationPermissionsShowPrompt(origin, callback);
            }

            // 多窗口的问题
            @Override
            public boolean onCreateWindow(WebView view, boolean isDialog, boolean isUserGesture, Message
                    resultMsg) {
                WebViewTransport transport = (WebViewTransport) resultMsg.obj;
                transport.setWebView(view);
                resultMsg.sendToTarget();
                return true;
            }
        });

        // 屏蔽掉长按事件 因为webview长按时将会调用系统的复制控件
        setOnLongClickListener(new OnLongClickListener() {

            @Override
            public boolean onLongClick(View v) {
                return true;
            }
        });
    }

    @Override
    public boolean canGoBack() {

        boolean canBack = super.canGoBack();

        if (canBack) {
            htmlCache.removeLast();
            if (null != listener) {
                listener.onWebPageChange(htmlCache.size());
            }
        }

        return canBack;
    }

    public void setOnWebLoadChangeListener(OnWebChangeListener listener) {
        this.listener = listener;
    }

    /**
     * 加载一个网页
     */
    public void loadHtmlFromNet(String url) {
        loadUrl(url);
    }

    /**
     * 加载apk包中的html页面
     */
    public void loadHtmlFromAssets(String htmlName) {
        loadUrl("file:///android_asset/" + htmlName);
    }

    /**
     * 加载手机本地的html页面
     */
    public void loadHtmlFromCard(String htmlName) {
        loadUrl("content://com.android.htmlfileprovider/sdcard/" + htmlName);
    }
}
